package base

import (
	"bytes"
	"database/sql"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"time"

	"gorm.io/gorm/schema"
)

//WorkPath 获取工作路径
func WorkPath() (string, error) {
	// path := os.Getenv("PAS3PATH")
	// if path != "" {
	// 	return path, nil
	// }
	file, err := os.Getwd()
	if err != nil {
		return "", err
	}
	// file := filepath.Dir(os.Args[0])
	p, err := filepath.Abs(file)
	if err != nil {
		return "", err
	}
	return p, nil
}

//GetFirstDateOfMonth 获取传入的时间所在月份的第一天，即某月第一天的0点。如传入time.Now(), 返回当前月份的第一天0点时间。
func GetFirstDateOfMonth(d *time.Time) *time.Time {
	date := d.AddDate(0, 0, -d.Day()+1)
	return GetZeroTime(&date)
}

//GetLastDateOfMonth 获取传入的时间所在月份的最后一天，即某月最后一天的0点。如传入time.Now(), 返回当前月份的最后一天0点时间。
func GetLastDateOfMonth(d *time.Time) *time.Time {
	date := GetFirstDateOfMonth(d).AddDate(0, 1, -1)
	return &date
}

//GetZeroTime 获取某一天的0点时间
func GetZeroTime(d *time.Time) *time.Time {
	date := time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, time.Local)
	return &date
}

//InStringSlice 判断字符串是否在切片中
func InStringSlice(str string, strSli []string) bool {
	for _, s := range strSli {
		if s == str {
			return true
		}
	}
	return false
}

//GetBackDATE 获取系统追溯日期
func GetBackDATE(types ...string) (date *time.Time, err error) {
	var bday, bdtp int
	err = MYDB.Raw(`select "BDAY","BDTP" from "T_CONF_UPDT"`).Row().Scan(&bday, &bdtp)
	if err != nil {
		return
	}
	var sysDate *time.Time
	sysDate, _, err = GetSYSDate("ODS")
	if err != nil {
		return
	}
	if len(types) == 1 && types[0] == "shls" {
		bdtp = 4
	}
	var bdate time.Time
	if bdtp == 1 {
		bdate = sysDate.AddDate(0, 0, -bday)
	} else if bdtp == 2 {
		bdate = sysDate.AddDate(0, 0, -bday)
		bdate = time.Date(bdate.Year(), bdate.Month(), 1, 0, 0, 0, 0, time.Local)
	} else if bdtp == 3 {
		b := sysDate.AddDate(0, 0, -bday)
		bd, _ := GetQuarterDay(b)
		bdate, _ = time.Parse("2006-01-02", bd)
	} else if bdtp == 4 {
		bdate = sysDate.AddDate(0, 0, -bday)
		bdate = time.Date(bdate.Year(), 1, 1, 0, 0, 0, 0, time.Local)
	} else {
		err = fmt.Errorf("表T_CONF_UPDT的字段BDTP只能是1,2,3或者4！")
	}
	date = &bdate
	return
}
func GetBackDateSH() (date *time.Time, err error) {
	var sysDate *time.Time
	var bdate time.Time
	sysDate, _, err = GetSYSDate("ODS")
	if err != nil {
		return
	}
	bd, _ := GetQuarterDay(*sysDate)
	bdate, _ = time.Parse("2006-01-02", bd)
	date = &bdate
	return
}

//获得当前季度的初始和结束日期
func GetQuarterDay(d time.Time) (string, string) {
	year := d.Format("2006")
	month := int(d.Month())
	var firstOfQuarter string
	var lastOfQuarter string
	if month >= 1 && month <= 3 {
		//1月1号
		firstOfQuarter = year + "-01-01"
		lastOfQuarter = year + "-03-31"
	} else if month >= 4 && month <= 6 {
		firstOfQuarter = year + "-04-01"
		lastOfQuarter = year + "-06-30"
	} else if month >= 7 && month <= 9 {
		firstOfQuarter = year + "-07-01"
		lastOfQuarter = year + "-09-30"
	} else {
		firstOfQuarter = year + "-10-01"
		lastOfQuarter = year + "-12-31"
	}
	return firstOfQuarter, lastOfQuarter
}

//ToString interface 转化为string
func ToString(value interface{}) string {
	switch tVal := value.(type) {
	case float32, float64:
		return fmt.Sprintf("%g", tVal)
	case int, int32, int64:
		return fmt.Sprintf("%d", tVal)
	case bool:
		return fmt.Sprintf("%t", tVal)
	case []byte:
		return string(tVal)
	case time.Time:
		t := value.(time.Time)
		return t.Format("20060102")
	case string:
		return tVal
	default:
		return fmt.Sprintf("%v", tVal)
	}
}

//PathExists 判断文件或者文件夹是否存在
func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

//IsInSlice 判断项目是否在切片中
func IsInSlice(arrs []string, item string) bool {
	for _, a := range arrs {
		if a == item {
			return true
		}
	}
	return false
}
func IsInSliceForInt(arrs []int, item int) bool {
	for _, a := range arrs {
		if a == item {
			return true
		}
	}
	return false
}

//IsBlank 判断值是否为空
func IsBlank(value reflect.Value) bool {
	return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())
}

//Exists 路径是否存在
func Exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

func LookUpFieldName(s *schema.Schema, fname string) string {
	for _, f := range s.Fields {
		if strings.EqualFold(f.DBName, fname) || strings.EqualFold(f.Name, fname) {
			return f.DBName
		}
	}
	return fname
}

func LookUpField(s *schema.Schema, fname string) *schema.Field {
	for _, f := range s.Fields {
		if strings.EqualFold(f.DBName, fname) || strings.EqualFold(f.Name, fname) {
			return f
		}
	}
	return nil
}

//ParseWhereCause 解析where条件
func ParseWhereCause(whereCauses []WhereCause) (whereStr string, paramVals []interface{}, err error) {
	if len(whereCauses) == 0 {
		return "", nil, nil
	}
	var whereBuf bytes.Buffer
	paramVals = make([]interface{}, 0, 10)
	for i, cause := range whereCauses {
		whereBuf.WriteString("(")
		for j, item := range cause.WhereItems {
			itemWhere, vals, err := parseWhereItem(&item)
			if err != nil {
				return "", nil, err
			}
			whereBuf.WriteString(itemWhere)
			if l := len(cause.WhereItems); j < l-1 {
				whereBuf.WriteString(" ")
				whereBuf.WriteString(cause.RelationSign.String())
				whereBuf.WriteString(" ")
			}
			paramVals = append(paramVals, vals...)
		}
		whereBuf.WriteString(")")
		if l := len(whereCauses); i < l-1 {
			whereBuf.WriteString(" AND ")
		}
	}
	whereStr = whereBuf.String()
	return
}

//parseWhereItem 解析WhereItem
func parseWhereItem(item *WhereItem) (string, []interface{}, error) {
	var buf bytes.Buffer
	buf.WriteString(item.FieldName)
	itemVals := item.FieldValues
	vals := make([]interface{}, 0, 10)
	switch item.Operator {
	case "like":
		buf.WriteString(" like ?")
		vals = append(vals, "%"+itemVals+"%")
	case "leftlike":
		buf.WriteString(" like ?")
		vals = append(vals, itemVals+"%")
	case "rightlike":
		buf.WriteString(" like ?")
		vals = append(vals, "%"+itemVals)
	case "notlike":
		buf.WriteString(" not like ?")
		vals = append(vals, "%"+itemVals+"%")
	case "in":
		buf.WriteString(" in (")
		items := strings.Split(itemVals, ",")
		for _, item := range items {
			buf.WriteString("?,")
			vals = append(vals, item)
		}
		if l := buf.Len(); l > 0 {
			buf.Truncate(l - 1)
		}
		buf.WriteString(")")
	case "notin":
		buf.WriteString(" not in (")
		items := strings.Split(itemVals, ",")
		for _, item := range items {
			buf.WriteString("?,")
			vals = append(vals, item)
		}
		if l := buf.Len(); l > 0 {
			buf.Truncate(l - 1)
		}
		buf.WriteString(")")
	case "between":
		buf.WriteString(" between ? and ?")
		vals2 := strings.Split(itemVals, ",")
		if l := len(vals2); l < 2 {
			errInfo := fmt.Sprintf("列%s的段值参数必须提供以逗号隔开的两个值！", item.FieldName)
			return "", nil, errors.New(errInfo)
		}
		vals = append(vals, vals2[0])
		vals = append(vals, vals2[1])
	case "null":
		buf.WriteString(" is null ")
	case "empty":
		buf.WriteString(" in (null,?) ")
		vals = append(vals, "")
	default:
		buf.WriteString(" ")
		buf.WriteString(item.Operator)
		buf.WriteString(" ? ")
		vals = append(vals, itemVals)
	}
	return buf.String(), vals, nil
}

//GetColumns 获取Sql执行之后的列值
func GetColumns(rows *sql.Rows) ([]string, error) {
	cols, err := rows.Columns()
	if err != nil {
		return nil, err
	}
	cLen := len(cols)
	for i := 0; i < cLen; i++ {
		cols[i] = strings.ToUpper(cols[i])
	}
	return cols, nil
}

/*
输入两个数组，一个旧数组，一个新数组
获取新数组相对旧数组所有新增和删除的元素
src：旧数组
dest：新数组
返回值：added 新增元素、deleted 删除的元素
*/
func Arrcmp(src, dest []int) ([]int, []int) {
	msrc := make(map[int]byte) //按原数组建索引
	mall := make(map[int]byte) //原+目 建立所有的索引
	var set []int              //交集
	for _, v := range src {
		msrc[v] = 0
		mall[v] = 0
	}
	for _, v := range dest {
		l := len(mall)
		mall[v] = 1
		if l != len(mall) {
			l = len(mall)
		} else {
			set = append(set, v)
		}
	}
	for _, v := range set {
		delete(mall, v)
	}
	var added, deleted []int
	for v, _ := range mall {
		_, exist := msrc[v]
		if exist {
			deleted = append(deleted, v)
		} else {
			added = append(added, v)
		}
	}
	return added, deleted
}
func IndoTM(idno string) string {
	ilen := len(idno)
	if ilen >= 18 {
		valBytes := []byte(idno)
		idno = string(valBytes[:6]) + "**********" + string(valBytes[16:])
	} else if ilen >= 6 {
		valBytes := []byte(idno)
		bval2 := make([]byte, ilen)
		for i := 0; i < ilen-6; i++ {
			bval2[i] = '*'
		}
		idno = string(valBytes[:6]) + string(bval2)
	}
	return idno
}
func CdnoTM(cdno string) string {
	ilen := len(cdno)
	if ilen >= 16 {
		valBytes := []byte(cdno)
		cdno = string(valBytes[:6]) + "******" + string(valBytes[12:])
	}
	return cdno
}
func AcnoTM(acno string) string {
	ilen := len(acno)
	valBytes := []byte(acno)
	if ilen >= 13 {
		acno = string(valBytes[:6]) + "******" + string(valBytes[12:])
	} else {
		acno = string(valBytes[:ilen-2]) + "**"
	}
	return acno
}
func PhoneTM(phno string) string {
	ilen := len(phno)
	valBytes := []byte(phno)
	if ilen > 5 {
		phno = string(valBytes[:ilen-5]) + "*****"
	} else {
		phno = "*****"
	}
	return phno
}

func LogToDB(uid int, pid int, ucont string, ip string) {
	sqlStr := `insert into t_UseLog(UserID,UseTime,PermID,use_content,ip_addr) values(?,?,?,?,?)`
	utime := time.Now().Local()
	err := MYDB.Exec(sqlStr, uid, utime, pid, ucont, ip).Error
	if err != nil {
		Logger.Error(fmt.Sprintf("写入日志出错:%s", err.Error()))
	}
}
